﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Data;

namespace Model
{
    [Serializable()]
    public class Winkel : ISerializable
    {
        #region Fields
        private static Winkel singleton;//Singleton implementatie. Slechts 1 instantie van winkel        
        private List<Rek> rekken;//Lijst van rekken binnen de winkel        
        private Image plattegrond;//Plattegrond van de winkel
        private Algoritme algoritme;//Het geselecteerde algoritme
        private int reknummer;//Het nummer van het rek
        public AbstracteLaag abstracteLaag;//Abstracte laag van algoritme
        private List<string> categoriën;//De lijst van categorieën
        private DataSet naamId;//De lijst van namen en id
        private string winkelNaam;
        #endregion

        /// <summary>
        /// Methode voor de fields toe te voegen aan het serializen
        /// </summary>
        /// <param name="info"></param>
        /// <param name="ctxt"></param>
        public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
        {
            info.AddValue("Rekken", this.rekken);
            info.AddValue("Singleton", singleton);
            info.AddValue("Plattegrond", this.plattegrond);
            //info.AddValue("Algoritme", this.algoritme);
            info.AddValue("Reknummer", this.reknummer);
            info.AddValue("AbstracteLaag", this.abstracteLaag);
            info.AddValue("Categorien", this.categoriën);
            info.AddValue("WinkelNaam", this.winkelNaam);
        }

        #region Constructors en Singleton
        /// <summary>
        /// Constructor voor het deserializen
        /// </summary>
        /// <param name="info"></param>
        /// <param name="ctxt"></param>
        private Winkel(SerializationInfo info, StreamingContext ctxt)
        {
            singleton = (Winkel)info.GetValue("Singleton", typeof(Winkel));
            rekken = (List<Rek>)info.GetValue("Rekken", typeof(List<Rek>));
            plattegrond = (Image)info.GetValue("Plattegrond", typeof(Image));
            //algoritme = (Algoritme)info.GetValue("Algoritme", typeof(Algoritme));
            reknummer = (int)info.GetValue("Reknummer", typeof(int));
            abstracteLaag = (AbstracteLaag)info.GetValue("AbstracteLaag", typeof(AbstracteLaag));
            categoriën = (List<string>)info.GetValue("Categorien", typeof(List<string>));
            winkelNaam = (string)info.GetValue("WinkelNaam", typeof(string));
        }

        /// <summary>
        /// Standaard constructor
        /// </summary>
        private Winkel()
        {
            reknummer = 0;
            rekken = new List<Rek>();
            abstracteLaag = AbstracteLaag.GetInstance();
        }

        /// <summary>
        /// Constructor van klasse winkel
        /// </summary>
        /// <param name="Plattegrond"></param>
        private Winkel(Image Plattegrond)
        {
            reknummer = 0;
            rekken = new List<Rek>();
            this.plattegrond = Plattegrond;
            abstracteLaag = AbstracteLaag.GetInstance();
        }



        /// <summary>
        /// Handelt de Singleton af met een image
        /// </summary>
        /// <param name="Plattegrond">Image van plattegrond</param>
        /// <returns></returns>
        public static Winkel GetInstance(Image P1)
        {
            if (singleton == null)
            {
                singleton = new Winkel(P1);
            }
            return singleton;
        }

        public static Winkel Instance
        {
            set {
                singleton = value;
            }
        }

        /// <summary>
        /// Handelt de Singleton af
        /// </summary>       
        public static Winkel GetInstance()
        {
            if (singleton == null)
            {
                singleton = new Winkel();
            }
            return singleton;
        }
        #endregion

        #region Properties

        /// <summary>
        /// Property voor Rekken
        /// </summary>
        public List<Rek> Rekken
        {
            get { return rekken; }
            set
            {
                rekken = value;
            }
        }

        /// <summary>
        /// Property voor plattegrond image
        /// </summary>
        public Image Plattegrond
        {
            get { return plattegrond; }
            set { plattegrond = value; }
        }

        public List<string> Categoriën
        {
            get { return categoriën; }
            set
            {
                categoriën = value;
                ModelP2 p2 = ModelP2.GetInstance();
                p2.AlleCategoriën = categoriën;
                p2.lanceerKlaar();
            }
        }

        /// <summary>
        /// property voor de winkelnaam
        /// </summary>
        public string WinkelNaam
        {
            get { return winkelNaam; }
            set { winkelNaam = value; }
        }

        public Winkelwagen BesteUitkomst
        {
            get { return besteUitkomst; }
            set { besteUitkomst = value; }
        }

        #endregion

        #region Toevoegen
        /// <summary>
        /// Voegt rek toe aan winkel
        /// </summary>
        /// <param name="punten">De punten van het rek</param>
        public void VoegRekToe(Point[] punten)
        {
            VoegRekToe(new Rek(punten, reknummer));
        }

        /// <summary>
        /// Voegt een rek toe aan de winkel
        /// </summary>
        /// <param name="punten">De coordinaten van het rek</param>
        public void VoegRekToe(List<Point> punten)
        {
            Point[] punte = punten.ToArray();
            VoegRekToe(punte);
        }

        /// <summary>
        /// Voegt een rek toe op basis van de punten van het gegeven rek.
        /// Verder data van het gegeven rek wordt niet opgenomen!
        /// </summary>
        /// <param name="rek"></param>
        public void VoegRekToe(Rek rek)
        {
            rek.Nummer = reknummer++;
            rekken.Add(rek);
        }

        /// <summary>
        /// Voegt artikel toe aan de winkel
        /// </summary>
        /// <param name="rek">ID van rek</param>
        /// <param name="zijde">ID van zijde</param>
        /// <param name="schap">ID van schap</param>
        /// <param name="artikel">Het toe te voegen artikel</param>
        public void VoegArtikelToe(int rek, int zijde, int schap, Artikel artikel)
        {
            rekken[rek].Zijden[zijde].Schappen[schap].Artikelen.Add(artikel);
        }

        /// <summary>
        /// Voegt schap toe aan winkel
        /// </summary>
        /// <param name="rek">ID van rek</param>
        /// <param name="zijde">ID van zijde</param>
        /// <param name="schap">Het toe te voegen schap</param>
        public void VoegSchapToe(int rek, int zijde, Schap schap)
        {
            rekken[rek].Zijden[zijde].Schappen.Add(schap);
        }
        #endregion

        #region Verwijderen
        /// <summary>
        /// Verwijdert een rek uit de winkel
        /// </summary>
        /// <param name="rek">ID van rek</param>
        public void VerwijderRek(int rek)
        {
            rekken.RemoveAt(rek);
            for (int i = rek; i < rekken.Count; i++)
            {
                rekken[i].Nummer = rekken[i].Nummer - 1;
            }
        }

        /// <summary>
        /// Verwijdert artikel
        /// </summary>
        /// <param name="rek">ID van rek</param>
        /// <param name="zijde">ID van zijde</param>
        /// <param name="schap">ID van schap</param>
        /// <param name="artikel">Het te verwijderen artikel</param>
        public void VerwijderArtikel(int rek, int zijde, int schap, Artikel artikel)
        {
            rekken[rek].Zijden[zijde].Schappen[schap].Artikelen.Remove(artikel);
        }

        /// <summary>
        /// Verwijdert een schap
        /// </summary>
        /// <param name="rek">ID van rek</param>
        /// <param name="zijde">ID van zijde</param>
        /// <param name="schap">ID van het te verwijderen schap</param>
        public void VerwijderSchap(int rek, int zijde, int schap)
        {
            rekken[rek].Zijden[zijde].VerwijderSchap(schap);
        }
        #endregion

        #region Algoritme
        public void StopAlgoritme()
        {
           algoritme.StopAlgoritme();
        }

        /// <summary>
        /// Voert het algoritme uit
        /// </summary>
        /// <param name="snelheid">De snelheid in m/s</param>
        /// <param name="tijd">De tijd in seconden</param>
        /// <param name="bedrag">Het doelbedrag</param>
        /// <param name="factor">De factor van eurocent/sec</param>
        public void AlgoritmeUitvoeren(List<string> categoriën, double snelheid, int tijd, double bedrag, double factor)
        {
            abstracteLaag = AbstracteLaag.Instance;
            abstracteLaag.Categoriën = categoriën;
            abstracteLaag.AantalMeter = (int)snelheid * tijd;
            abstracteLaag.Snelheid = snelheid;
            abstracteLaag.Doelbedrag = bedrag;
            abstracteLaag.Factor = factor;
            BesteUitkomst = algoritme.AlgoritmeUitvoeren();
            ModelP2.GetInstance().StuurWagen(besteUitkomst);
           //ModelP2.GetInstance().lanceerKlaar();
        }

        /// <summary>
        /// Verandert het geselcteerde algoritme
        /// </summary>
        /// <param name="typeAlgoritme">Het type algoritme</param>
        public void VeranderAlgoritme(TypeAlgoritme typeAlgoritme)
        {
            switch (typeAlgoritme)
            {
                case (TypeAlgoritme.Tijd):
                    algoritme = Tijd.GetInstance();
                    break;
                case (TypeAlgoritme.Bedrag):
                    algoritme = Bedrag.GetInstance();
                    break;
            }
        }

        public void OplossingKlaar()
        {
            AInfo t = ModelP2.GetInstance();
            t.oplossingGevonden();
        }

        public void HelemaalKlaar()
        {
            AInfo t = ModelP2.GetInstance();
            t.HelemaalKlaar();
        }

        private Winkelwagen besteUitkomst;

        #endregion

        #region AbstracteLaag Samenstellen
        /// <summary>
        /// Sluit een categorie uit
        /// </summary>
        /// <param name="categorie">De categorie</param>
        public void SluitCategorieUit(string categorie)
        {
            //abstracteLaag.UitgeslotenCategorieën.Add(categorie);
        }

        //hier moet alles in de abstracte laag
        #endregion

        internal void AlgoritmeStoppen()
        {
            StopAlgoritme();
        }
    }

    /// <summary>
    /// Lijst met objecten die opgeslagen moet worden
    /// </summary>
    [Serializable()]
    public class ObjectToSerialize : ISerializable
    {
        private Winkel winkel;

        public Winkel Winkel
        {
            get { return this.winkel; }
            set { this.winkel = value; }
        }

        public ObjectToSerialize(SerializationInfo info, StreamingContext ctxt)
        {
            this.winkel = (Winkel)info.GetValue("Winkel", typeof(Winkel));
        }

        public ObjectToSerialize()
        {
        }

        public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
        {
            info.AddValue("Winkel", this.winkel);
        }
    }
}
